/**
 * @file   encode.hpp
 * @author wolfhead <wolfhead@wolfhead-laptop>
 * @date   Fri Aug 26 09:25:30 2011
 * 
 * @brief this will be a refactor for 
 *        the old encoder.hpp and charset.hpp  
 * 
 * 
 */

#ifndef ACAPELLA_STRING_ENCODE_HPP
#define ACAPELLA_STRING_ENCODE_HPP

#include "string_util.hpp"
#include "encode_detail.hpp"


namespace acapella
{

namespace charset
{
struct ascii : virtual __detail__::_charset<char> {};
struct utf8 : virtual __detail__::_charset<char> {};
struct urlencode : virtual __detail__::_charset<char> {};
}//namespace charset 

template<typename CharSetFrom, typename CharSetTo>
class EncoderImpl{};

template<typename CharSetFrom, typename CharSetTo>
class Encoder
{
public:
    typedef typename CharSetFrom::traits::const_pointer_type input_type;
    typedef typename CharSetFrom::traits::string_type input_string_type;
    typedef typename CharSetTo::traits::string_type& output_type;
    typedef typename CharSetTo::traits::string_type return_type;

   
    static void Encode(input_type fromStr, output_type toStr)
    {
        EncoderImpl<CharSetFrom, CharSetTo>::Encode(fromStr, toStr);
    }

    static return_type Encode(input_type fromStr)
    {
        return_type tmp;
        Encode(fromStr, tmp);
        return tmp;
    }

    static return_type Encode(const input_string_type& fromStr)
    {
        return_type tmp;
        Encode(fromStr.c_str(), tmp);
        return tmp;
    }
};


template<>
class EncoderImpl<charset::utf8, charset::urlencode>
{
public:
    static void Encode(
        charset::utf8::traits::const_pointer_type input,
        charset::urlencode::traits::string_type& output)
    {
        size_t inputLen = charset::utf8::traits::strlen(input);
        unsigned char high;
        unsigned char low;

        output.clear();
        output.reserve(inputLen);

        charset::utf8::traits::const_pointer_type current = input;
        charset::utf8::traits::const_pointer_type end = input + inputLen;
        for (; current != end; ++current)
        {
            if (!__detail__::__UrlEncoding::isUrlCharacter(*current))
            {
                output.append(input, current - input);

                StringUtil::Bin2Hex(*current, high, low);

                output.append(1, '%').append(1, high).append(1, low);

                input = current + 1;
            }
        }

        output.append(input, current - input);
        
        return;
    }
};


template<>
class EncoderImpl<charset::urlencode, charset::utf8>
{
public:
    static void Encode(
        charset::urlencode::traits::const_pointer_type input,
        charset::utf8::traits::string_type& output)
    {
        size_t inputLen = charset::utf8::traits::strlen(input);
        unsigned char bin;

        output.clear();
        output.reserve(inputLen);

        charset::utf8::traits::const_pointer_type current = input;
        charset::utf8::traits::const_pointer_type end = input + inputLen;
        for (; current != end; ++current)
        {
            if (*current == '%')
            {
                output.append(input, current - input);

                if (current + 2 < end)
                {
                    StringUtil::Hex2Bin(*(current + 1), *(current + 2), bin);
                    output.append(1, bin);
                    input = current + 3;
                    current = current + 2;
                }

                input =  current + 1;
            }
        }

        output.append(input, current - input);
        
        return;
    }
};

}//namespace acapella

#endif
